#include <chrono>
#include <exception>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <unistd.h>
#include <vector>

#include "config.hpp"
#include "decode_h264.hpp"
#include "header.hpp"
#include "logger.hpp"
#include "muxer_mp4.hpp"
#include "utils.hpp"

#define PRINT_H264_INFO 0
#define PRINT_FRAME_INFO 0
#define PRINT_DETECT_INFO 0
#define PRINT_DECODE_MS 0

/**
 * @brief 构造函数
 */
decode_h264::decode_h264()
{
    this->rbuf = nullptr;
}

/**
 * @brief 构造函数
 * @param ringbuffer 环形缓存区
 */
decode_h264::decode_h264(ringbuffer* ringbuffer, std::string ip)
{
    if (nullptr == ringbuffer) {
        return;
    }

    this->ip = ip;
    this->rbuf = ringbuffer;
    utils::folder_create(CUT_VIDEO_SAVE_PATH + ip, true);
}

/**
 * @brief 析构函数
 */
decode_h264::~decode_h264()
{
}

/**
 * @brief 设置接口
 */
int decode_h264::set(ringbuffer* ringbuffer, std::string ip, std::vector<std::vector<int>> regions)
{
    if (nullptr == ringbuffer) {
        return -1;
    }

    this->ip = ip;
    this->rbuf = ringbuffer;
    utils::folder_create(CUT_VIDEO_SAVE_PATH + ip, true);

    shields = regions;

    return 0;
}

/**
 * @brief 主线程，解封装视频流，解析得到的 H264 存放在环形缓冲区中
 */
void decode_h264::thread()
{
    int ret = 0;
    int cnt = 0;
    int save_cnt = 0;
    int true_cnt = 0; // 连续 true 的计数器
    int false_cnt = 0; // 连续 false 的计数器
    bool started = false; // 是否已经发送过 start 命令
    std::chrono::system_clock::time_point start_time;

    if (nullptr == this->rbuf) {
        return;
    }

    AVCodec* codec = avcodec_find_decoder(AV_CODEC_ID_H264);
    if (!codec) {
        Logger::error("Camera {} decode, Codec not found", ip);
        return;
    }

    AVCodecContext* codec_ctx = avcodec_alloc_context3(codec);
    if (!codec_ctx) {
        Logger::error("Camera {} decode, Failed to allocate AVCodecContext", ip);
        return;
    }

    AVBufferRef* hw_device_ctx = nullptr;
    if (av_hwdevice_ctx_create(&hw_device_ctx, AV_HWDEVICE_TYPE_VAAPI, nullptr, nullptr, 0) < 0) {
        Logger::error("Camera {} decode, Failed to create VAAPI device", ip);
        avcodec_free_context(&codec_ctx);
        return;
    }

    codec_ctx->hw_device_ctx = av_buffer_ref(hw_device_ctx);
    if (avcodec_open2(codec_ctx, codec, nullptr) < 0) {
        Logger::error("Camera {} decode, Could not open codec", ip);
        av_buffer_unref(&hw_device_ctx);
        avcodec_free_context(&codec_ctx);
        return;
    }

    /* 这个视频帧是内存中的帧 */
    AVFrame* frame = av_frame_alloc();
    if (!frame) {
        Logger::error("Camera {} decode, Failed to allocate frame!", ip);
        av_buffer_unref(&hw_device_ctx);
        avcodec_free_context(&codec_ctx);
        return;
    }

    /* 这个视频帧是硬件解码器引擎中的帧，不在内存，需要转化后才能访问 */
    AVFrame* hw_frame = av_frame_alloc();
    if (!hw_frame) {
        Logger::error("Camera {} decode, Failed to allocate hardware frame!", ip);
        av_frame_free(&frame);
        av_buffer_unref(&hw_device_ctx);
        avcodec_free_context(&codec_ctx);
        return;
    }

    AVPacket packet = {};
    int ridx = 0;

    while (running) {
        rbuf->lock();

        int curr = ridx;
        header_t head;
        ret = rbuf->readr(&ridx, (uint8_t*)&head, sizeof(header_t));
        if (ret) {
            rbuf->unlock();
            msleep(40);
            continue;
        }

        if (head.sync[0] != 'H' || head.sync[1] != 'E' || head.sync[2] != 'A' || head.sync[3] != 'D') {
            rbuf->unlock();
            Logger::error("Camera {} decode, Sync code not match", ip);
            std::terminate();
            continue;
        }

        packet.data = (uint8_t*)av_malloc(head.size);
        packet.size = head.size;
        packet.pts = head.pts;
        packet.dts = head.dts;
        packet.flags = head.type;
        ret = rbuf->readr(&ridx, packet.data, packet.size);
        if (ret) {
            rbuf->unlock();
            msleep(40);
            continue;
        }

        rbuf->unlock();

        /* 只有成功拿到 H.264 码流才会打印，并且打印的是当前帧的缓存区位置 */
        // Logger::debug("Camera {} [{}] decode, read index = {}", ip, cnt, curr);

#if PRINT_H264_INFO
        std::cout << std::setfill(' ')
                  << std::setw(16) << ip
                  << std::setw(10) << packet.pts
                  << std::setw(8) << packet.size
                  << std::setw(4) << packet.flags
                  << std::hex
                  << std::setfill('0')
                  << "  0x"
                  << std::setw(2) << static_cast<int>(packet.data[0])
                  << std::setw(2) << static_cast<int>(packet.data[1])
                  << std::setw(2) << static_cast<int>(packet.data[2])
                  << std::setw(2) << static_cast<int>(packet.data[3])
                  << std::setw(2) << static_cast<int>(packet.data[4])
                  << std::setw(2) << static_cast<int>(packet.data[5])
                  << std::setw(2) << static_cast<int>(packet.data[6])
                  << std::setw(2) << static_cast<int>(packet.data[7])
                  << std::setw(2) << static_cast<int>(packet.data[packet.size - 4])
                  << std::setw(2) << static_cast<int>(packet.data[packet.size - 3])
                  << std::setw(2) << static_cast<int>(packet.data[packet.size - 2])
                  << std::setw(2) << static_cast<int>(packet.data[packet.size - 1])
                  << " Dec"
                  << std::dec
                  << std::endl;
#endif

#if PRINT_DECODE_MS
        // 记录解码开始时间
        auto start_time = std::chrono::high_resolution_clock::now();
#endif

        ret = avcodec_send_packet(codec_ctx, &packet);
        if (ret < 0) {
            Logger::error("Camera {} decode, Error sending packet for decoding", ip);
            av_free(packet.data);
            msleep(40);
            break;
        }
        av_free(packet.data);

        ret = avcodec_receive_frame(codec_ctx, hw_frame);
        if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
            Logger::error("Camera {} decode, continue", ip);
            msleep(40);
            continue;
        } else if (ret < 0) {
            Logger::error("Camera {} decode, Error during decoding", ip);
            msleep(40);
            continue;
        }

        /* 每 10 帧做一次运动检测 */
        if (0 == ++cnt % 10) {
            /* 将硬件解码的帧数据转移到普通内存中 */
            if (av_hwframe_transfer_data(frame, hw_frame, 0) < 0) {
                Logger::error("Camera {} decode, Failed to transfer hardware frame data!", ip);
                msleep(40);
                continue;
            }

            /* 屏蔽区域数据设置为 0 */
            for (const auto& s : shields) {
                rect_shield(frame, s[0], s[1], s[2], s[3]);
            }

            /* 保存第 30 帧的图像为 BMP，方便查看屏蔽区域的设置效果 */
            save_cnt++;
            if (save_cnt == 3) {
                utils::folder_create(SHIELD_SAVE_PATH, true);
                std::string name = SHIELD_SAVE_PATH + ip + ".bmp";
                save_shield(frame, name);
            }

            /* 对内存中的数据做运动检测 */
            bool result = mdet.detect(frame);

#if PRINT_DETECT_INFO
            std::cout << std::setfill(' ')
                      << std::setw(16) << ip
                      << std::setw(10) << frame->pts
                      << std::setw(8) << cnt
                      << std::setw(4) << result
                      << std::endl;
#endif

            /* 记录连续运动或者连续静止计数值 */
            if (result) {
                true_cnt++;
                false_cnt = 0;
            } else {
                false_cnt++;
                true_cnt = 0;
            }

            /* 连续 3 次运动则开始保存运动视频 */
            if (!started && true_cnt >= 3) {
                started = true;
                mux->cut(COMMAND_START, frame->pts);
                start_time = std::chrono::system_clock::now();
            }

            if (started) {
                auto now = std::chrono::system_clock::now();
                auto duration = std::chrono::duration_cast<std::chrono::seconds>(now - start_time).count();

                /* 连续 10 次静止则结束保存运动视频，或者说是时长超过1分钟也会结束保存 */
                if (false_cnt >= 10) {
                    started = false;
                    mux->cut(COMMAND_STOP, frame->pts);
                } else if (duration >= CUT_VIDEO_MAX_TIME) {
                    started = false;
                    true_cnt = 0;
                    mux->cut(COMMAND_STOP, frame->pts);
                }
            }
        }

#if PRINT_DECODE_MS
        // 记录解码结束时间
        auto end_time = std::chrono::high_resolution_clock::now();

        // 计算解码时间
        std::chrono::duration<double> duration = end_time - start_time;
        std::cout << ip << " " << packet.flags << " Decoded frame " << duration.count() * 1000 << " ms" << std::endl;
#endif
    }

    av_frame_free(&frame);
    av_frame_free(&hw_frame);
    av_buffer_unref(&hw_device_ctx);
    avcodec_free_context(&codec_ctx);
}

/**
 * @brief 启动主线程
 */
void decode_h264::start()
{
    if (!handle.joinable()) {
        running = true;
        handle = std::thread(&decode_h264::thread, this);
    }
}

/**
 * @brief 关闭主线程
 */
void decode_h264::stop()
{
    if (handle.joinable()) {
        running = false;
        handle.join();
    }
}

/**
 * @brief 设置 MP4 封装器
 */
void decode_h264::mux_set(muxer_mp4* mux)
{
    this->mux = mux;
}

/**
 * @brief 填充 NV12 中的指定矩形区域为 0
 */
void decode_h264::rect_shield(AVFrame* frame, int x1, int y1, int w, int h)
{
    if (!frame || frame->format != AV_PIX_FMT_NV12) {
        Logger::error("Camera {} decode, rect_shield only supports NV12 format", ip);
        return;
    }

    int width = frame->width;
    int height = frame->height;

    // 1. 填充 Y 平面 (亮度)
    for (int y = y1; y < y1 + h; y++) {
        if (y >= height)
            break;
        memset(frame->data[0] + y * frame->linesize[0] + x1, 0, w);
    }

    // 2. 填充 UV 平面 (色度)
    // UV 平面宽度是 Y 平面的一半，高度是 Y 平面的一半
    int uv_width = (width + 1) / 2; // UV 平面宽度
    int uv_height = (height + 1) / 2; // UV 平面高度
    for (int y = y1 / 2; y < (y1 + h) / 2; y++) { // UV 的 y 轴范围是 Y 的一半
        if (y >= uv_height)
            break;
        // 填充 UV 分量的每行（注意 U 和 V 数据交替存储）
        memset(frame->data[1] + y * frame->linesize[1] + x1, 0, w); // 填充 U 和 V 的交替数据
    }
}

/**
 * @brief 保存屏蔽处理后的视频帧，用于判断是否生效，保存为 bmp
 */
void decode_h264::save_shield(AVFrame* frame, std::string filename)
{
    if (!frame || frame->format != AV_PIX_FMT_NV12) {
        Logger::error("Camera {} decode, rect_shield only supports NV12 format", ip);
        return;
    }

    // 获取图像宽高
    int width = frame->width;
    int height = frame->height;

    // 创建一个转换器，用于将 YUV 转换为 BGR
    SwsContext* sws_ctx = sws_getContext(width, height, (AVPixelFormat)frame->format,
        width, height, AV_PIX_FMT_BGR24,
        SWS_BICUBIC, nullptr, nullptr, nullptr);
    if (!sws_ctx) {
        Logger::error("Camera {} decode, Error creating SwsContext", ip);
        return;
    }

    // 创建一个新的 AVFrame 用于存储 BGR 数据
    AVFrame* rgb_frame = av_frame_alloc();
    if (!rgb_frame) {
        Logger::error("Camera {} decode, Error allocating BGR frame", ip);
        sws_freeContext(sws_ctx);
        return;
    }

    // 分配 BGR 数据内存
    int rgb_size = av_image_get_buffer_size(AV_PIX_FMT_RGB24, width, height, 1);
    uint8_t* rgb_data = (uint8_t*)av_malloc(rgb_size);
    if (!rgb_data) {
        Logger::error("Camera {} decode, Error allocating memory for BGR data", ip);
        av_frame_free(&rgb_frame);
        sws_freeContext(sws_ctx);
        return;
    }

    // 设置 BGR frame 的数据指针
    av_image_fill_arrays(rgb_frame->data, rgb_frame->linesize, rgb_data, AV_PIX_FMT_RGB24, width, height, 1);

    // 执行转换
    sws_scale(sws_ctx, frame->data, frame->linesize, 0, height, rgb_frame->data, rgb_frame->linesize);

    // 释放转换上下文
    sws_freeContext(sws_ctx);

    // 保存为 BMP 文件
    std::ofstream bmp_file(filename, std::ios::binary);
    if (!bmp_file.is_open()) {
        Logger::error("Camera {} decode, Error opening BMP file for writing", ip);
        av_free(rgb_data);
        av_frame_free(&rgb_frame);
        return;
    }

    // BMP 文件头（14字节）
    uint8_t bmp_header[14] = {
        0x42, 0x4D, // BM
        0, 0, 0, 0, // 文件大小
        0, 0, // 保留字段
        0, 0, // 保留字段
        54, 0, 0, 0 // 数据偏移量
    };

    // BMP 信息头（40字节）
    uint8_t info_header[40] = {
        40, 0, 0, 0, // 信息头大小
        0, 0, 0, 0, // 宽度
        0, 0, 0, 0, // 高度
        1, 0, // 色彩平面数
        24, 0, // 每像素位数
        0, 0, 0, 0, // 压缩方式
        0, 0, 0, 0, // 数据大小
        0, 0, 0, 0, // 水平分辨率
        0, 0, 0, 0, // 垂直分辨率
        0, 0, 0, 0, // 色彩索引
        0, 0, 0, 0 // 重要颜色
    };

    // 填写 BMP 文件头和信息头
    int file_size = 54 + rgb_size; // 文件总大小
    bmp_header[2] = (file_size) & 0xFF;
    bmp_header[3] = (file_size >> 8) & 0xFF;
    bmp_header[4] = (file_size >> 16) & 0xFF;
    bmp_header[5] = (file_size >> 24) & 0xFF;

    info_header[4] = (width) & 0xFF;
    info_header[5] = (width >> 8) & 0xFF;
    info_header[6] = (width >> 16) & 0xFF;
    info_header[7] = (width >> 24) & 0xFF;
    info_header[8] = (height) & 0xFF;
    info_header[9] = (height >> 8) & 0xFF;
    info_header[10] = (height >> 16) & 0xFF;
    info_header[11] = (height >> 24) & 0xFF;

    // 写入文件头和信息头
    bmp_file.write(reinterpret_cast<char*>(bmp_header), 14);
    bmp_file.write(reinterpret_cast<char*>(info_header), 40);

    // 写入图像数据
    for (int y = height - 1; y >= 0; --y) { // BMP 图像数据从底到顶
        bmp_file.write(reinterpret_cast<char*>(rgb_frame->data[0] + y * rgb_frame->linesize[0]), width * 3);
    }

    // 释放资源
    bmp_file.close();
    av_free(rgb_data);
    av_frame_free(&rgb_frame);

    Logger::info("Camera {} decode, saved shielded area to bmp", ip);
}

/**
 * @brief 毫秒延时
 */
void decode_h264::msleep(int ms)
{
    std::this_thread::sleep_for(std::chrono::milliseconds(ms));
}
